package com.himaci.gtbattle.model;

import java.util.ArrayList;

/**
 * Kelas untuk menampung aksi
 *
 * @author XNPSDN
 */
public class Action {

    private static Action instance;
    public ArrayList<Action> p1ActionList;
    public ArrayList<Action> p2ActionList;
    public int playerNumber;
    public int executorNumber;
    public int actionNumber;
    public int targetNumber;

    /**
     * Konstruktor
     * @param playerNumber Nomor player
     * @param executorNumber Character, 1-3
     * @param actionNumber Action, 1-3
     * @param targetNumber Target, 1-3
     */
    private Action(int playerNumber, int executorNumber, int actionNumber, int targetNumber) {
	this.playerNumber = playerNumber;
	this.executorNumber = executorNumber;
	this.actionNumber = actionNumber;
	this.targetNumber = targetNumber;
    }

    /**
     * Konstruktor
     */
    private Action() {
	p1ActionList = new ArrayList<>();
	p2ActionList = new ArrayList<>();

	// player 1	
	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 0, 0)); // critical attack
	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 0, 1));
	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 0, 2));

	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 1, 0)); // focus fire
	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 1, 1));
	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 1, 2));

	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 2, 0)); // mana burn
	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 2, 1));
	p1ActionList.add(new Action(Player.PLAYER_ONE, 0, 2, 2));

	p1ActionList.add(new Action(Player.PLAYER_ONE, 1, 0, 0)); // curse

	p1ActionList.add(new Action(Player.PLAYER_ONE, 1, 1, 0)); // silence
	p1ActionList.add(new Action(Player.PLAYER_ONE, 1, 1, 1));
	p1ActionList.add(new Action(Player.PLAYER_ONE, 1, 1, 2));

	p1ActionList.add(new Action(Player.PLAYER_ONE, 1, 2, 0)); // meteor

	p1ActionList.add(new Action(Player.PLAYER_ONE, 2, 0, 0)); // inc def

	p1ActionList.add(new Action(Player.PLAYER_ONE, 2, 1, 0)); // heal
	p1ActionList.add(new Action(Player.PLAYER_ONE, 2, 1, 1));
	p1ActionList.add(new Action(Player.PLAYER_ONE, 2, 1, 2));

	p1ActionList.add(new Action(Player.PLAYER_ONE, 2, 2, 0)); // inc power

	// player 2	
	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 0, 0)); // critical attack
	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 0, 1));
	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 0, 2));

	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 1, 0)); // focus fire
	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 1, 1));
	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 1, 2));

	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 2, 0)); // mana burn
	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 2, 1));
	p2ActionList.add(new Action(Player.PLAYER_TWO, 0, 2, 2));

	p2ActionList.add(new Action(Player.PLAYER_TWO, 1, 0, 0)); // curse

	p2ActionList.add(new Action(Player.PLAYER_TWO, 1, 1, 0)); // silence
	p2ActionList.add(new Action(Player.PLAYER_TWO, 1, 1, 1));
	p2ActionList.add(new Action(Player.PLAYER_TWO, 1, 1, 2));

	p2ActionList.add(new Action(Player.PLAYER_TWO, 1, 2, 0)); // meteor

	p2ActionList.add(new Action(Player.PLAYER_TWO, 2, 0, 0)); // inc def

	p2ActionList.add(new Action(Player.PLAYER_TWO, 2, 1, 0)); // heal
	p2ActionList.add(new Action(Player.PLAYER_TWO, 2, 1, 1));
	p2ActionList.add(new Action(Player.PLAYER_TWO, 2, 1, 2));

	p2ActionList.add(new Action(Player.PLAYER_TWO, 2, 2, 0)); // inc power
    }

    /**
     * Mendapatkan singleton instance
     * @return Instance dari kelas Action
     */
    public static synchronized Action getInstance() {
	if (instance == null) {
	    instance = new Action();
	}
	return instance;
    }

    /**
     * Apakah skill dapat dieksekusi
     * @return Bisa atau tidak
     */
    public boolean canExecute(int playerNumber, int charNumber, int skillNumber, int targetNumber, World world) {
	Action act = new Action(playerNumber, charNumber, skillNumber, targetNumber);

	if (playerNumber == Player.PLAYER_ONE) {
	    for (Action action : p1ActionList) {
		if (act.isEqual(action)) {
		    act = action;
		    break;
		}
	    }
	} else {
	    for (Action action : p2ActionList) {
		if (act.isEqual(action)) {
		    act = action;
		    break;
		}
	    }
	}

	return act.canExecute(world);
    }

    /**
     * Eksekusi aksi
     */
    public Action execute(int playerNumber, int charNumber, int skillNumber, int targetNumber, World world) {
	Action act = new Action(playerNumber, charNumber, skillNumber, targetNumber);

	if (playerNumber == Player.PLAYER_ONE) {
	    for (Action action : p1ActionList) {
		if (act.isEqual(action)) {
		    act = action;
		    break;
		}
	    }
	} else {
	    for (Action action : p2ActionList) {
		if (act.isEqual(action)) {
		    act = action;
		    break;
		}
	    }
	}

	act.execute(world);
	return act;
    }

    /**
     * Aksi yang diambil sama atau tidak
     * @param action Aksi yang ingin dibandingkan
     * @return Sama atau tidak
     */
    public boolean isEqual(Action action) {
	// kalau targetnya ga penting
	if (playerNumber == action.playerNumber) {
	    // mage
	    if (executorNumber == action.executorNumber && executorNumber == 1) {
		if (actionNumber == action.actionNumber && actionNumber == 0) {
		    return true;
		}
		if (actionNumber == action.actionNumber && actionNumber == 2) {
		    return true;
		}
	    }
	    // priest
	    if (executorNumber == action.executorNumber && executorNumber == 2) {
		if (actionNumber == action.actionNumber && actionNumber == 0) {
		    return true;
		}
		if (actionNumber == action.actionNumber && actionNumber == 2) {
		    return true;
		}
	    }
	}

	// kalau targetnya penting
	return playerNumber == action.playerNumber
		&& executorNumber == action.executorNumber
		&& actionNumber == action.actionNumber
		&& targetNumber == action.targetNumber;
    }

    /**
     * Apakah skill dapat dieksekusi
     * @param world Kondisi world
     * @return Bisa atau tidak
     */
    public boolean canExecute(World world) {
	GameChar executor;
	GameChar target;

	// cegah exception
	if (executorNumber < 0 || executorNumber > 2 || targetNumber < 0 || targetNumber > 2) {
	    return false;
	}

	if (Skill.getSkill(executorNumber, actionNumber) == Skill.HEAL_SKILL) {
	    // kalau heal
	    if (playerNumber == 1) {
		executor = world.player1.characters[executorNumber];
		target = world.player1.characters[targetNumber];
	    } else {
		executor = world.player2.characters[executorNumber];
		target = world.player2.characters[targetNumber];
	    }
	} else {
	    // skill lain
	    if (playerNumber == 1) {
		executor = world.player1.characters[executorNumber];
		target = world.player2.characters[targetNumber];
	    } else {
		executor = world.player2.characters[executorNumber];
		target = world.player1.characters[targetNumber];
	    }
	}
	return executor.skills[actionNumber].canExecute(executor, target);
    }

    /**
     * Eksekusi aksi
     * @param world Kondisi world
     */
    public void execute(World world) {
	GameChar executor;
	GameChar target;

	if (Skill.getSkill(executorNumber, actionNumber) == Skill.HEAL_SKILL) {
	    // kalau heal
	    if (playerNumber == 1) {
		executor = world.player1.characters[executorNumber];
		target = world.player1.characters[targetNumber];
	    } else {
		executor = world.player2.characters[executorNumber];
		target = world.player2.characters[targetNumber];
	    }
	} else {
	    // skill lain
	    if (playerNumber == 1) {
		executor = world.player1.characters[executorNumber];
		target = world.player2.characters[targetNumber];
	    } else {
		executor = world.player2.characters[executorNumber];
		target = world.player1.characters[targetNumber];
	    }
	}
	//executor.skills[actionNumber].execute(executor, target);
	executor.skill(target, executor.skills[actionNumber]);
    }

    /**
     * Mengambil aksi yang mungkin
     * @param playerNumber Player number
     * @return Koleksi aksi yang mungkin
     */
    public ArrayList<Action> getActionList(int playerNumber) {
	if (playerNumber == Player.PLAYER_ONE) {
	    return p1ActionList;
	} else {
	    return p2ActionList;
	}
    }

    /**
     * Bentuk string dari aksi
     * @return Bentuk string
     */
    public String display() {
	String retVal = "";
	retVal += "Player " + playerNumber;
	retVal += " - E: ";
	switch (executorNumber) {
	    case 0:
		retVal += "Archer";
		break;
	    case 1:
		retVal += "Mage";
		break;
	    case 2:
		retVal += "Priest";
		break;
	}
	retVal += " - " + Skill.getSkill(executorNumber, actionNumber).skillName;
	retVal += " - T: ";
	switch (targetNumber) {
	    case 0:
		retVal += "Archer";
		break;
	    case 1:
		retVal += "Mage";
		break;
	    case 2:
		retVal += "Priest";
		break;
	}
	return retVal;
    }
}
